package cn.edu.zut.hhr.service.impl;

import cn.edu.zut.hhr.consts.ArticleConsts;
import cn.edu.zut.hhr.entity.*;
import cn.edu.zut.hhr.entity.dos.Archive;
import cn.edu.zut.hhr.mapper.ArticleBodyMapper;
import cn.edu.zut.hhr.mapper.ArticleMapper;
import cn.edu.zut.hhr.mapper.TbUserArticleMapper;
import cn.edu.zut.hhr.service.*;
import cn.edu.zut.hhr.utils.RedisUtil;
import cn.edu.zut.hhr.utils.UserUtil;
import cn.edu.zut.hhr.vo.ArticleVo;
import cn.edu.zut.hhr.vo.RestResponse;
import cn.edu.zut.hhr.vo.enums.TopEnum;
import cn.edu.zut.hhr.vo.params.ArticleParams;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.bouncycastle.jcajce.provider.symmetric.AES;
import org.springframework.beans.BeanUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 文章表 服务实现类
 * </p>
 *
 * @author HHR
 * @since 2021-08-21
 */
@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {

    @Resource
    private ArticleMapper articleMapper;

    @Resource
    private TagService tagService;

    @Resource
    private UserService userService;

    @Resource
    private ArticleBodyMapper articleBodyMapper;

    @Resource
    private ArticleTagService articleTagService;

    @Resource
    private TbUserArticleMapper userArticleMapper;

//    @Resource
    private ArticleChooseService chooseService;

    @Resource
    private RedisUtil redisUtil;

    private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

    @Override
    public List<ArticleVo> getArticleList(int page, int count) {
        IPage<Article> articlePage = new Page<>(page,count);
        LambdaQueryWrapper<Article> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.orderByDesc(Article::getTop,Article::getGmtCreate);
        articleMapper.selectPage(articlePage,queryWrapper);
        List<Article> articles = articlePage.getRecords();
        List<ArticleVo> articleVos = getArticleVoList(articles,false);
        return articleVos;
    }

    @Override
    public List<ArticleVo> getHotArticleList(int count) {
        IPage<Article> articlePage = new Page<>(1,count);
        LambdaQueryWrapper<Article> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.orderByDesc(Article::getViewCounts);
        articleMapper.selectPage(articlePage,queryWrapper);
        List<Article> articles = articlePage.getRecords();
        List<ArticleVo> articleVos = getArticleVoList(articles,false);
        return articleVos;
    }

    @Override
    public List<ArticleVo> getNewArticleList(int lastIdx, int count) {
        IPage<Article> articlePage = new Page<>(1,count);
        LambdaQueryWrapper<Article> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.lt(Article::getId,lastIdx)
                .orderByDesc(Article::getId);
        articleMapper.selectPage(articlePage,queryWrapper);
        List<Article> articles = articlePage.getRecords();
        List<ArticleVo> articleVos = getArticleVoList(articles,false);
        return articleVos;
    }

    @Override
    public List<Archive> getListArchives() {
        List<Archive> archives = articleMapper.getListArchives();
        return archives;
    }

    @Override
    public RestResponse getArticleById(int id) {
        Article article = articleMapper.selectById(id);
        if(article == null){
            return RestResponse.fail("不存在文章");
        }
        ArticleVo articleVo = getArticleVo(article,true);
        return RestResponse.success(articleVo);
    }

    @Override
    public void updateMany(List<Article> list) {
        articleMapper.updateMany(list);
    }

    @Override
    @Transactional
    public RestResponse addArticle(ArticleParams articleParams) {
        completeArticle(articleParams);
        ArticleBody articleBody = new ArticleBody();
        Article article = new Article();

        articleBody.setHtmlContent(articleParams.getContent());
        articleBodyMapper.insert(articleBody);
        article.setBid(articleBody.getId());

        User user = UserUtil.currentUser();
        BeanUtils.copyProperties(articleParams,article);
        article.setUid(user.getId());
        article.setAuthorName(user.getName());
        article.setViewCounts(0);
        article.setLikeCounts(0);
        article.setCommentCounts(0);
        article.setTop(TopEnum.SIMPLE.getVal());
        articleBody.setDel(0);

        articleMapper.insert(article);

        List<Tag> tags = getTagsByName(articleParams.getTags());
        tagService.insertMany(tags);

        List<ArticleTag> articleTags = getArticleTags(article.getId(),tags);
        articleTagService.insertMany(articleTags);

        return RestResponse.success(article.getId());
    }

    @Override
    public RestResponse getArticleByTid(Integer tid) {
        LambdaQueryWrapper<Article> queryWrapper = Wrappers.lambdaQuery();
        List<Long> aids = new ArrayList<>();
        List<ArticleTag> articleTags = articleTagService.getArticleTagsByTid(tid);
        if(articleTags != null && articleTags.size() != 0){
            articleTags.forEach(articleTag -> {
                Long aid = articleTag.getAid();
                if(aid != null){
                    aids.add(aid);
                }
            });
        }
        queryWrapper.in(Article::getId,aids);
        List<Article> articles = articleMapper.selectList(queryWrapper);
        List<ArticleVo> articleVos = getArticleVoList(articles,false);
        return RestResponse.success(articleVos);
    }

    @Override
    public RestResponse getArticleListByTime(Integer year, Integer month, Integer page,Integer pageSize) {
        IPage<Article> iPage = new Page<>(page,pageSize);
        List<Article> articles = articleMapper.getArticleListByTime(iPage,year,month);
        List<ArticleVo> articleVos = getArticleVoList(articles,false);
        return RestResponse.success(articleVos);
    }

    @Override
    public RestResponse getRandomArticle(User user, int count) {
        List<Article> articles = null;
        if(user == null){
            articles = chooseService.chooseArticle(count);
        } else{
            articles = chooseService.chooseArticle(user.getId(),count);
        }
        if(articles == null){
            return RestResponse.fail("文章选择错误, 请稍后再试");
        }
        return RestResponse.success(articles);
    }

    @Override
    public RestResponse likeArticle(long uid, long aid) {
        long now = System.currentTimeMillis();
        boolean ok = redisUtil.zSetAdd(ArticleConsts.LikePre+uid,aid,now);
        if(!ok){
            return RestResponse.success("repeat like");
        }
        long nowCnt = redisUtil.incr(ArticleConsts.ArticlePre+aid);
        if(nowCnt == 0){
            return RestResponse.fail("update redis fail");
        }
        return RestResponse.success(nowCnt);
    }

    @Override
    public RestResponse saveArticle(Long uid, Long aid) {
        TbUserArticle userArticle = new TbUserArticle(aid,uid);
        LambdaUpdateWrapper<TbUserArticle> wrapper = Wrappers.lambdaUpdate();
        int insert = userArticleMapper.insertNotExist(userArticle);
        if(insert == 0){
            return RestResponse.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(),"save err");
        }
        return RestResponse.success(userArticle.getId());
    }

    @Override
    public RestResponse getSaveArtByUid(Long uid, Integer page, Integer pageSize) {
        IPage<Article> pg = new Page<>(page,pageSize,pageSize);
        pg = articleMapper.selectArtByUid(uid, pg);
        return RestResponse.success(pg.getRecords());
    }

    private List<Tag> getTagsByName(List<String> names){
        List<Tag> tags = new ArrayList<>();
        names.forEach(name -> {
            Tag tag = new Tag();
            tag.setName(name);
            tags.add(tag);
        });
        return tags;
    }

    private List<ArticleTag> getArticleTags(Long aid, List<Tag> tags) {
        List<ArticleTag> articleTags = new ArrayList<>();
        tags.forEach(tag -> {
            ArticleTag articleTag = new ArticleTag();
            articleTag.setAid(aid);
            articleTag.setTid(tag.getId());
            articleTags.add(articleTag);
        });
        return articleTags;
    }

    private String completeArticle(ArticleParams articleParams) {
        if(articleParams.getTitle() == null){
            return "文章标题不能为空";
        }
        if(articleParams.getContent() == null){
            return "文章内容不能为空";
        }
        if(articleParams.getSummary() == null){
            articleParams.setSummary("");
        }
        if(articleParams.getTags() == null){
            articleParams.setTags(new ArrayList<>());
        }
        return null;
    }

    /**
     * 根据数据库中文章封装成前端需要的文章
     * @param articles
     * @param hasBody 是否带有文章内容
     * @return
     */
    private List<ArticleVo> getArticleVoList(List<Article> articles,boolean hasBody){
        List<ArticleVo> articleVos = new ArrayList<>();
        for(Article article: articles){
            ArticleVo articleVo = getArticleVo(article,hasBody);
            articleVos.add(articleVo);
        }
        return articleVos;
    }

    private ArticleVo getArticleVo(Article article, boolean hasBody){
        ArticleVo articleVo = new ArticleVo();
        articleVo.setArticle(article);
        User user = userService.getById(article.getUid());
        if(user == null){
            user = new User();
            user.setName("未知");
        }
        articleVo.setUser(user);
        articleVo.setTags(tagService.getByAId(article.getId()));
        articleVo.setCreateTime(sdf.format(article.getGmtCreate()));
        if(hasBody){
            ArticleBody articleBody = articleBodyMapper.selectById(article.getBid());
            articleVo.setBody(articleBody);
        }
        return articleVo;
    }

}
